package com.agmte.api.config.filter;


import com.agmte.api.app.entity.ApiGroup;
import com.agmte.api.app.entity.ApiInfo;
import com.agmte.api.app.entity.pojo.ApiMapper;
import com.agmte.api.app.entity.pojo.ApiParamInfo;
import com.agmte.api.app.entity.pojo.ParamDataType;
import com.agmte.api.common.crud.core.CrudDb;
import com.agmte.api.common.utils.JsonBuilder;
import com.agmte.api.common.utils.StrUtils;
import com.agmte.api.common.utils.UrlUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Component("Knife4ApiScannerA7a64fa4")
public class Knife4ApiScanner {


    public final static JSONObject modelMap = new JSONObject();
    public final static JSONObject paths = new JSONObject();
    public final static JSONArray tags = new JSONArray();


    public void apiScanner() {
        modelMap.clear();
        paths.clear();
        tags.clear();

        List<ApiGroup> apiGroups = CrudDb.list(ApiGroup.class);
        Map<Long, String> apiGroupsGro = apiGroups.stream().collect(Collectors.toMap(ApiGroup::getId, ApiGroup::getName));
        apiGroupsGro.put(0L, "未定义分组");

        List<ApiInfo> apiInfos = CrudDb.list(ApiInfo.class);
        Map<Long, List<ApiInfo>> apiInfosGroup = apiInfos.stream()
                .peek(apiInfo -> apiInfo.setGid(apiInfo.getGid() != null ? apiInfo.getGid() : 0L))
                .collect(Collectors.groupingBy(ApiInfo::getGid));

        // 设置标签
        for (Map.Entry<Long, List<ApiInfo>> entry : apiInfosGroup.entrySet()) {
            Long key = entry.getKey();
            List<ApiInfo> apiInfoList = entry.getValue();

            String groupName = apiGroupsGro.get(key) == null ? apiGroupsGro.get(0L) : apiGroupsGro.get(key);

            tags.add(JsonBuilder.pair("name", groupName, "description", groupName));

            this.apiDescription(apiInfoList, groupName);

        }
    }


    private void apiDescription(List<ApiInfo> apiInfos, String groupName) {
        for (ApiInfo apiInfo : apiInfos) {

            String unique = UrlUtil.urlToCamel(apiInfo.getPath());

            parseApiInfo(apiInfo, unique);

            String url = UrlUtil.join("/agmte/custom/api/get/", apiInfo.getPath());

            JSONObject post = JsonBuilder.first("summary", apiInfo.getName())
                    .put("description", apiInfo.getName())
                    .put("operationId", StrUtils.concatWithCapitalization("Api", unique))
                    .put("responses", JsonBuilder.pair("200", JsonBuilder.pair("description", "成功")))
                    .put("parameters", Arrays.asList(
                            JsonBuilder.first("in", "body")
                                    .put("name", "json")
                                    .put("description", "参数")
                                    .put("required", true)
                                    .put("schema", JsonBuilder.pair("originalRef", unique, "$ref", "#/definitions/" + unique))
                                    .build()
                    ))
                    .put("tags", Collections.singletonList(groupName))
                    .put("consumes", Collections.singletonList("application/json"))
                    .build();
            paths.put(url, JsonBuilder.pair("post", post));
        }

    }

    private void parseApiInfo(ApiInfo apiInfo, String modelRef) {

        List<ApiMapper> mappers = apiInfo.getMappers();
        JSONObject def = new JSONObject();
        def.put("type", "object");
        def.put("title", apiInfo.getName());

        JSONObject properties = new JSONObject();

        for (ApiMapper mapper : mappers) {
            String originalRef = StrUtils.concatWithCapitalization(modelRef, mapper.getCode());
            properties.put(mapper.getCode(),
                    JsonBuilder.pair(
                            "originalRef", originalRef,
                            "$ref", "#/definitions/" + originalRef,
                            "description", mapper.getName()
                    )

            );
            parseParam(mapper, originalRef);
        }
        def.put("properties", properties);
        modelMap.put(modelRef, def);

    }

    private void parseParam(ApiMapper mapper, String modelRef) {

        JSONObject def = new JSONObject();
        def.put("type", "object");
        def.put("title", mapper.getName());

        JSONObject properties = new JSONObject();
        for (ApiParamInfo paramInfo : mapper.getParamInfos()) {
            JSONObject type = this.getType(paramInfo.getDatatype());
            type.put("name", paramInfo.getCode());
            type.put("description", paramInfo.getName());
            type.put("required", paramInfo.isRequired());
            if(paramInfo.getDatatype().name().contains("array")){
                if(paramInfo.getExample() instanceof String){
                    type.put("example",((String) paramInfo.getExample()).split(","));
                }
            }else {
                type.put("example", paramInfo.getExample());
            }

            properties.put(paramInfo.getCode(), type);
        }
        def.put("properties", properties);
        modelMap.put(modelRef, def);
    }

    private JSONObject getType(ParamDataType paramDataType) {
        if (paramDataType == null) {
            return new JSONObject();
        }
        switch (paramDataType) {
            case array_int:
                return JsonBuilder.first("type", "array")
                        .put("items", JsonBuilder.pair("type", "integer", "format", "int64"))
                        .build();
            case array_string:
                return JsonBuilder.first("type", "array")
                        .put("items", JsonBuilder.pair("format", "string"))
                        .build();
            case array_double:
                return JsonBuilder.first("type", "array")
                        .put("items", JsonBuilder.pair("type", "double", "format", "number"))
                        .build();
            case integer:
                return JsonBuilder.pair("type", "integer");
            case Double:
                return JsonBuilder.pair("type", "double", "format", "number");
            case string:
            default:
                return JsonBuilder.pair("type", "string");
        }
    }

}